package com.yy.crypto;

import lombok.extern.log4j.Log4j2;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.util.Base64;

/**
 * 解密微信的数据
 * @Author: lyy
 * Date: 2021/8/9 16:42
 */
@Log4j2
public class WxUserInfoDecryptUtil {

	public static final  String AES = "AES";
	/**
	 * 加解密算法/模式/填充方式
	 * ECB模式只用密钥即可对数据进行加密解密，CBC模式需要添加一个iv
	 */
	public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";

	/**
	 * 微信 数据解密<br/>
	 * 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充<br/>
	 * 对称解密的目标密文:encrypted=Base64_Decode(encryptData)<br/>
	 * 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节<br/>
	 * 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节<br/>
	 *
	 * @param encrypted 目标密文
	 * @param sessionKey 会话ID
	 * @param iv 加密算法的初始向量
	 */
	public static String wxDecrypt(String encrypted, String sessionKey, String iv) throws Exception {
		byte[] encrypted64 = Base64.getDecoder().decode(encrypted);
		byte[] sessionKey64 = Base64.getDecoder().decode(sessionKey);
		byte[] iv64 = Base64.getDecoder().decode(iv);
		String json;
		try {
			init();
			json = new String(decrypt(encrypted64, sessionKey64, generateIV(iv64)));
		} catch (Exception e){
			throw e;
		}
		return json;
	}

	/**
	 * 初始化密钥
	 * @throws NoSuchAlgorithmException
	 */
	private static void init() throws NoSuchAlgorithmException {
		Security.addProvider(new BouncyCastleProvider());
		KeyGenerator.getInstance(AES).init(128);
	}

	/**
	 * 生成iv
	 * @param iv
	 * @return
	 * @throws InvalidParameterSpecException
	 * @throws NoSuchAlgorithmException
	 */
	private static AlgorithmParameters generateIV(byte[] iv) throws InvalidParameterSpecException, NoSuchAlgorithmException {
		AlgorithmParameters instance = AlgorithmParameters.getInstance(AES);
		instance.init(new IvParameterSpec(iv));
		return instance;
	}

	/**
	 * 生成解密数据
	 * @param encryptedData
	 * @param keyBytes
	 * @param iv
	 * @return
	 * @throws InvalidAlgorithmParameterException
	 * @throws InvalidKeyException
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	private static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException {
		Key key = new SecretKeySpec(keyBytes, AES);
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, key, iv);
		return cipher.doFinal(encryptedData);
	}

}
